package net.onceface.web.oauth2.domain;

import java.io.Serializable;
import java.time.LocalDateTime;

import net.onceface.web.utils.DateUtils;

/**
 * Oauth 客户端详细信息
 * **/
public class OauthClientDetails implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3144133441821338352L;
	
	/**
	 * 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段)
	 * **/
	private LocalDateTime createTime = DateUtils.now();
	
	/**
	 * 用于标识客户端是否已存档(即实现逻辑删除),默认值为'0'(即未存档). 
	 * 字段的具体使用请参考CustomJdbcClientDetailsService.java,在该类中,扩展了在查询client_details的SQL加上archived = 0条件 (扩展字段)
	 * **/
    private boolean archived = false;

    /**
     * 主键,必须唯一,不能为空. 
     * 用于唯一标识每一个客户端(client); 在注册时必须填写(也可由服务端自动生成). 
     * 对于不同的grant_type,该字段都是必须的. 在实际应用中的另一个名称叫appKey,与client_id是同一个概念.
     * **/
    private String clientId;
    
    /**
     * 客户端所能访问的资源id集合,多个资源时用逗号(,)分隔,如: "unity-resource,mobile-resource". 
     * 字段的值必须来源于标签‹oauth2:resource-server的属性resource-id值一致.
     * **/
    private String resourceIds;

    /**
     * 用于指定客户端(client)的访问密匙; 在注册时必须填写(也可由服务端自动生成). 
     * 对于不同的grant_type,该字段都是必须的. 在实际应用中的另一个名称叫appSecret,与client_secret是同一个概念.
     * **/
    private String clientSecret;
    
    /**
     * 可用值：read,write
     * **/
    private String scope;
    
    /**
     * grant types包括："authorization_code", "password", "assertion", "refresh_token"
     * 默认值是："authorization_code,refresh_token"
     * **/
    private String authorizedGrantTypes = "authorization_code,refresh_token";
    
    /**
     * 客户端的重定向URI,可为空, 当grant_type为authorization_code或implicit时, 
     * 在Oauth的流程中会使用并检查与注册时填写的redirect_uri是否一致. 
     * 注1.当grant_type=authorization_code时, 第一步 从 spring-oauth-server获取 'code'时客户端发起请求时必须有redirect_uri参数, 
     * 该参数的值必须与 web_server_redirect_uri的值一致. 第二步 用 'code' 换取 'access_token' 时客户也必须传递相同的redirect_uri. 
	 * 在实际应用中, web_server_redirect_uri在注册时是必须填写的, 一般用来处理服务器返回的code, 
	 * 验证state是否合法与通过code去换取access_token值. 在spring-oauth-client项目中, 
	 * 可具体参考AuthorizationCodeController.java中的authorizationCodeCallback方法.
	 * 注2.当grant_type=implicit时通过redirect_uri的hash值来传递access_token值.如:
	 * http://localhost:7777/spring-oauth-client/implicit#access_token=dc891f4a-ac88-4ba6-8224-a2497e013865&token_type=bearer&expires_in=43199
     * 然后客户端通过JS等从hash值中取到access_token值.
     * **/
    private String webServerRedirectUri;
    
    /**
     * 该字段必须要设置对应的权限值, 因为服务端将根据该字段值的权限来判断是否有权限访问对应的API. 
     * **/
    private String authorities;

    /**
     * 设定客户端的access_token的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 12, 12小时). 
     * 在服务端获取的access_token JSON数据中的expires_in字段的值即为当前access_token的有效时间值. 
	 * 在项目中, 可具体参考DefaultTokenServices.java中属性accessTokenValiditySeconds. 
	 * 在实际应用中, 该值一般是由服务端处理的, 不需要客户端自定义.
     * **/
    private Integer accessTokenValidity;
    
    /**
     * 设定客户端的refresh_token的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 24 * 30, 30天). 
     * 若客户端的grant_type不包括refresh_token,则不用关心该字段
     * 具体参考DefaultTokenServices.java中属性refreshTokenValiditySeconds
     * 该值一般是由服务端处理的, 不需要客户端自定义.
     * **/
    private Integer refreshTokenValidity;
    
    /**
     * 预留的字段,在Oauth的流程中没有实际的使用,可选,但若设置值,必须是JSON格式的数据,如:{"country":"CN","country_code":"086"}
     * 在实际应用中, 可以用该字段来存储关于客户端的一些其他信息,如客户端的国家,地区,注册时的IP地址等等.
     * **/
    private String additionalInformation;
    
    /**
     * 设置客户端是否为受信任的,默认为'0'(即不受信任的,1为受信任的). 
     * 该字段只适用于grant_type="authorization_code"的情况,当用户登录成功后,若该值为0,则会跳转到让用户Approve的页面让用户同意授权, 
     * 若该字段为1,则在登录后不需要再让用户Approve同意授权(因为是受信任的). 
     * 对该字段的具体使用请参考OauthUserApprovalHandler.java. (扩展字段)
     * */
    private boolean trusted = false;
    
    /**
     * 设置用户是否自动Approval操作, 默认值为 'false', 可选值包括 'true','false', 'read','write'. 
     * 该字段只适用于grant_type="authorization_code"的情况,当用户登录成功后,若该值为'true'或支持的scope值,
     * 则会跳过用户Approve的页面, 直接授权. 该字段与 trusted 有类似的功能, 是 spring-security-oauth2 的 2.0 版本后添加的新属性.
     * **/
    private String autoApprove;
    
    public OauthClientDetails(){}
    
    public OauthClientDetails(String clientId, String clientSecret, String resourceIds,
    		String authorizedGrantTypes, String scope){
    	this.clientId = clientId;
    	this.clientSecret = clientSecret;
    	this.resourceIds = resourceIds;
    	this.authorizedGrantTypes = authorizedGrantTypes;
    	this.scope = scope;
    }
    
	public LocalDateTime getCreateTime() {
		return createTime;
	}

	public void setCreateTime(LocalDateTime createTime) {
		this.createTime = createTime;
	}

	public String getAdditionalInformation() {
		return additionalInformation;
	}

	public void setAdditionalInformation(String additionalInformation) {
		this.additionalInformation = additionalInformation;
	}

	public boolean isArchived() {
		return archived;
	}

	public void setArchived(boolean archived) {
		this.archived = archived;
	}

	public String getClientId() {
		return clientId;
	}

	public void setClientId(String clientId) {
		this.clientId = clientId;
	}

	public String getResourceIds() {
		return resourceIds;
	}

	public void setResourceIds(String resourceIds) {
		this.resourceIds = resourceIds;
	}

	public String getClientSecret() {
		return clientSecret;
	}

	public void setClientSecret(String clientSecret) {
		this.clientSecret = clientSecret;
	}

	public String getScope() {
		return scope;
	}

	public void setScope(String scope) {
		this.scope = scope;
	}

	public String getAuthorizedGrantTypes() {
		return authorizedGrantTypes;
	}

	public void setAuthorizedGrantTypes(String authorizedGrantTypes) {
		this.authorizedGrantTypes = authorizedGrantTypes;
	}

	public String getWebServerRedirectUri() {
		return webServerRedirectUri;
	}

	public void setWebServerRedirectUri(String webServerRedirectUri) {
		this.webServerRedirectUri = webServerRedirectUri;
	}

	public String getAuthorities() {
		return authorities;
	}

	public void setAuthorities(String authorities) {
		this.authorities = authorities;
	}

	public Integer getAccessTokenValidity() {
		return accessTokenValidity;
	}

	public void setAccessTokenValidity(Integer accessTokenValidity) {
		this.accessTokenValidity = accessTokenValidity;
	}

	public Integer getRefreshTokenValidity() {
		return refreshTokenValidity;
	}

	public void setRefreshTokenValidity(Integer refreshTokenValidity) {
		this.refreshTokenValidity = refreshTokenValidity;
	}

	public boolean isTrusted() {
		return trusted;
	}

	public void setTrusted(boolean trusted) {
		this.trusted = trusted;
	}

	public String getAutoApprove() {
		return autoApprove;
	}

	public void setAutoApprove(String autoApprove) {
		this.autoApprove = autoApprove;
	}
    
	@Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("OauthClientDetails");
        sb.append("{createTime=").append(createTime);
        sb.append(", archived=").append(archived);
        sb.append(", clientId='").append(clientId).append('\'');
        sb.append(", resourceIds='").append(resourceIds).append('\'');
        sb.append(", clientSecret='").append(clientSecret).append('\'');
        sb.append(", scope='").append(scope).append('\'');
        sb.append(", authorizedGrantTypes='").append(authorizedGrantTypes).append('\'');
        sb.append(", webServerRedirectUri='").append(webServerRedirectUri).append('\'');
        sb.append(", authorities='").append(authorities).append('\'');
        sb.append(", accessTokenValidity=").append(accessTokenValidity);
        sb.append(", refreshTokenValidity=").append(refreshTokenValidity);
        sb.append(", additionalInformation='").append(additionalInformation).append('\'');
        sb.append(", trusted=").append(trusted);
        sb.append('}');
        return sb.toString();
    }
}
